using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Web.Security;
using Argotic.Syndication.Specialized;
using Dexter.Async.Services;
using Dexter.DataTransferObject;
using Dexter.Domain.Model;
using Dexter.Logger.Contracts;
using Dexter.Services.Helpers;
using Topics.Radical.Validation;

namespace Dexter.Services.Implementation {
	/// <summary>
	/// 	todo: missing / wrong Tags import/export
	/// </summary>
	public class ImportService : IImportService {
		readonly IAsyncService _asyncService;
		readonly ICategoryService _categegoryDataService;
		readonly ICommentService _commentDataService;
		readonly ILogger _logger;
		readonly IPostService _postDataService;

		public ImportService(ILogger logger,
		                     ICategoryService categegoryDataService,
		                     IPostService postDataService,
		                     ICommentService commentDataService, IAsyncService asyncService) {
			_logger = logger;
			_categegoryDataService = categegoryDataService;
			_postDataService = postDataService;
			_commentDataService = commentDataService;
			_asyncService = asyncService;
			_logger.Debug("Creating new ImportService");
		}

		#region IImportService Members

		public IAsyncResult ImportFromBlogMlAsync(byte[] file) {
			Ensure.That(file).IsNotNull();

			return _asyncService.AsyncCall(o => ImportFromBlogMl(o), file);
		}
		
		public ImportResultDto ImportFromBlogMl(object obj) {
			Ensure.That(obj).IsNotNull();

			_logger.Warn("BlogML import routine start.");

			ImportResultDto result = new ImportResultDto();

			try
			{
				byte[] file = (byte[])obj;

				BlogMLDocument document;
				using (Stream s = new MemoryStream(file))
				{
					document = new BlogMLDocument();
					document.Load(s);
				}


				Dictionary<string, string> users = ImportUsers(document);

				#region Add Categories

				Dictionary<string, Category> categories = ImportCategories(document);

				#endregion

				Category defaultCategory = _categegoryDataService.GetDefaultCategoryWithChilds();

				foreach (BlogMLPost post in document.Posts)
				{
					if (post.ApprovalStatus == BlogMLApprovalStatus.Approved)
					{
						string postTitle = BlogMlImportServiceHelpers.DecodeBlogMlTextConstructutContent(post.Title).DecodeHtml();

						_logger.WarnFormat("Processing post: {0}", postTitle);
					
						#region Add Posts

						_logger.Debug("Retriving the WLW tags from the body.");
						string[] tags = PostHelpers.RetrieveTagsFromBody(post.Content.Content);

						// Logger.Debug("Clean body from WLW tags.");
						// questionable, if we do this we cannot reopen and exit the post with WLW again
						// string body = Utility.RemovePlumbingFromWlwHtmlTags(post.Content.Content);

						// todo: check if the post exists and maybe update it

						string body = BlogMlImportServiceHelpers.DecodeBlogMlTextConstructutContent(post.Content);
						body = PostHelpers.PreProcessUnformattedMessages(body);

						Post pst = Post.CreateNewPost(postTitle, null, body, users[post.Authors[0]]);
						pst.PublishDate = post.CreatedOn;
						pst.BreakOnAggregate = body.Contains("[more]");
						pst.EnableComment();
						pst.Publish();
						if (tags != null && tags.Count() > 0)
						{
							tags.Where(x => !string.IsNullOrEmpty(x)).ForEach(x => pst.Tags.Add(TagItem.CreateNewTagItem(x, pst)));
						}

						// process multiple categories
						foreach (string category in post.Categories)
						{
							if (categories.ContainsKey(category))
							{
								Category cdto = categories[category];
								if (!pst.Categories.Contains(cdto))
									pst.AddCategory(cdto);
							}
						}
						// add a default categ if needed
						if (pst.Categories.Count == 0)
							pst.AddCategory(defaultCategory);

						_logger.WarnFormat("Saving Post: {0} - Url: {1}", postTitle, post.Url != null ? post.Url.ToString() : string.Empty);
						
						_postDataService.Save(pst);

						#endregion

						#region Add Comments

						foreach (BlogMLComment comment in post.Comments)
						{
							_logger.WarnFormat("Creating Comment for the post {0}.", pst.Id);
							Uri userWebsite = comment.UserUrl;
							MailAddress userEmail = BlogMlImportServiceHelpers.GetMailAddressSafe(comment.UserEmailAddress, _logger);
							string userName = string.IsNullOrEmpty(comment.UserName)
												? "anonymous"
												: comment.UserName;
							Comment cmt = Comment.CreateNewComment(
								pst,
								userWebsite,
								userEmail,
								BlogMlImportServiceHelpers.DecodeBlogMlTextConstructutContent(comment.Content),
								userName,
								null
								);
							cmt.Approved = comment.ApprovalStatus == BlogMLApprovalStatus.Approved;
							cmt.CreationDate = comment.CreatedOn;
							cmt.Notify = false;
							_logger.WarnFormat("Saving Comment for the post {0}.", pst.Id);
							_logger.WarnFormat("Comment: {0}", cmt.ToString());

							_commentDataService.Save(cmt);
						}

						#endregion
					}
				}
				_logger.Debug("Import Complete.");

				result.Succeded = true;
			}
			catch (Exception e)
			{
				_logger.Error("Unhandled exception was throw in the ImportService", e);

				result.ErrorMessage = e.Message;
				result.Succeded = false;
			}
			finally
			{
				_logger.Warn("BlogML import routine end.");
			}

			return result;
		}

		#endregion

		Dictionary<string, Category> ImportCategories(BlogMLDocument document) {
			Dictionary<string, Category> categories = new Dictionary<string, Category>();

			foreach (BlogMLCategory category in document.Categories) {
				Category cat = _categegoryDataService.GetCategoryByNameWithChilds(category.Title.Content);

				if (cat == null) {
					_logger.WarnFormat("Creating category {0}.", category.Title.Content);
					cat = Category.CreateNewCategory(category.Title.Content, null);
					_categegoryDataService.Save(cat);
					_logger.WarnFormat("Category {0} created.", category.Title.Content);
				}

				if (!categories.ContainsKey(category.Id))
					categories.Add(category.Id, _categegoryDataService.GetCategoryByNameWithChilds(category.Title.Content));
			}
			return categories;
		}

		Dictionary<string, string> ImportUsers(BlogMLDocument document) {
			Dictionary<string, string> users = new Dictionary<string, string>();

			foreach (BlogMLAuthor author in document.Authors) {
				if (Membership.GetUser(author.Title.Content) == null) {
					_logger.WarnFormat("Creating author {0}.", author.Title.Content);
					Membership.CreateUser(author.Title.Content, "d3xt3r", author.EmailAddress);

					_logger.WarnFormat("Adding author {0} to role Poster.", author.Title.Content);
					Roles.AddUsersToRole(new[]
					                     	{
					                     		author.Title.Content
					                     	}, "Poster");

					_logger.WarnFormat("Author {0} ready.", author.Title.Content);
				}

				if (!users.ContainsKey(author.Id))
					users.Add(author.Id, author.Title.Content);
			}

			return users;
		}
	}
}